﻿using System;
using System.Reflection;

namespace MyBasicLib.Reflection.FastReflection.Emit
{
    public class PropertyAccessor : IPropertyAccessor
    {
        private Func<object, object> m_getter;
        //private Action<object, object> m_setMethodInvoker;
        private IMethodInvoker m_setMethodInvoker;

        public PropertyAccessor(PropertyInfo propertyInfo)
        { 
            InitializeGet(propertyInfo);
            InitializeSet(propertyInfo);
        }

        #region IPropertyAccessor Members

        public object GetValue(object o)
        {
            return m_getter.Invoke(o);
        }

        public void SetValue(object o, object value)
        {
            m_setMethodInvoker.Invoke(o, value);
        }

        #endregion

        private void InitializeGet(PropertyInfo propertyInfo)
        {
            if (!propertyInfo.CanRead) return;
            MethodInfo mi = propertyInfo.GetGetMethod(true);
            var mii = new MethodInvoker(mi);
            m_getter = p => mii.Invoke(p);
            return;
            //Type t = propertyInfo.DeclaringType;


            //var dynamicMethod = new DynamicMethod(string.Empty,
            //                                      typeof (object), new[]
            //                                                           {
            //                                                               typeof (object)
            //                                                           },t.Module); //,t.Module);
            //ILGenerator il = dynamicMethod.GetILGenerator();
            //if (mi != null)
            //{
            //    if (!mi.IsStatic)
            //    {
            //        il.Emit(OpCodes.Ldarg_0);
            //        il.EmitCall(OpCodes.Callvirt, mi, null); //Get the property value    
            //    }
            //    else
            //    {
            //        il.Emit(OpCodes.Call, mi); //Get the property value    
            //    }

            //    il.EmitBoxIfNeeded(propertyInfo.PropertyType);
            //}
            //else
            //{
            //    il.ThrowException(typeof (MissingMethodException));
            //}

            //il.Emit(OpCodes.Ret);
            //m_getter = dynamicMethod.CreateDelegate(typeof (Func<object, object>)) as Func<object, object>;
        }

        private void InitializeSet(PropertyInfo propertyInfo)
        {
            if (!propertyInfo.CanWrite) return;

            if (!propertyInfo.CanWrite) return;
            MethodInfo methodInfo = propertyInfo.GetSetMethod(true);
            // this.m_setMethodInvoker = Caches.Get(methodInfo, p => new MethodInvoker(p));
            m_setMethodInvoker = new MethodInvoker(methodInfo);

            /*

            Type t = propertyInfo.DeclaringType;


            var dynamicMethod = new DynamicMethod(string.Empty,
                                                  null, new[]
                                                            {
                                                                typeof (object),
                                                                typeof (object)
                                                            },
                                                  t); //.Module);

            MethodInfo mi = propertyInfo.GetSetMethod(true);
            mi.
            ILGenerator il = dynamicMethod.GetILGenerator();
            if (mi != null)
            {
            
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldarg_1);
                il.EmitCastToReference(propertyInfo.PropertyType);
                il.EmitCall(OpCodes.Callvirt, mi, null); //Set the property value 
   
            }
            else
            {
                il.ThrowException(typeof (MissingMethodException));
            }

            il.Emit(OpCodes.Ret);

            m_setMethodInvoker = dynamicMethod.CreateDelegate(typeof (Action<object, object>)) as Action<object, object>;*/
        }
    }
}